import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Website } from './entities/website.entity';
import { PageView } from './entities/page-view.entity';
import { WebsiteQueryDto, TimeRange } from './dto/website-query.dto';

@Injectable()
export class StatsService {
  constructor(
    @InjectRepository(Website)
    private websiteRepository: Repository<Website>,
    @InjectRepository(PageView)
    private pageViewRepository: Repository<PageView>,
  ) {}

  private async getWebsiteById(id: number): Promise<Website> {
    const website = await this.websiteRepository.findOne({ where: { id } });
    if (!website) throw new Error('网站不存在');
    return website;
  }

  private getDateRange(timeRange: TimeRange, startDate?: string, endDate?: string): { startDate: Date, endDate: Date } {
    const now = new Date();
    let start: Date;
    let end: Date = now;
    if (startDate && endDate) {
      start = new Date(startDate);
      end = new Date(endDate);
    } else {
      switch (timeRange) {
        case TimeRange.TODAY:
          start = new Date(now.getFullYear(), now.getMonth(), now.getDate());
          break;
        case TimeRange.YESTERDAY:
          start = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1);
          end = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1, 23, 59, 59, 999);
          break;
        case TimeRange.LAST_7_DAYS:
          start = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
          break;
        case TimeRange.LAST_30_DAYS:
          start = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);
          break;
        case TimeRange.THIS_MONTH:
          start = new Date(now.getFullYear(), now.getMonth(), 1);
          break;
        case TimeRange.LAST_MONTH:
          start = new Date(now.getFullYear(), now.getMonth() - 1, 1);
          end = new Date(now.getFullYear(), now.getMonth(), 0, 23, 59, 59, 999);
          break;
        default:
          start = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
      }
    }
    return { startDate: start, endDate: end };
  }

  private async getPageViewsByDay(queryBuilder, startDate: Date, endDate: Date): Promise<any[]> {
    return queryBuilder
      .select('DATE(pv.createdAt) as date')
      .addSelect('COUNT(*) as count')
      .addSelect('COUNT(DISTINCT pv.sessionId) as uniqueCount')
      .groupBy('DATE(pv.createdAt)')
      .orderBy('date', 'ASC')
      .getRawMany();
  }
  private async getPageViewsByWeek(queryBuilder, startDate: Date, endDate: Date): Promise<any[]> {
    return queryBuilder
      .select('YEARWEEK(pv.createdAt) as week')
      .addSelect('COUNT(*) as count')
      .addSelect('COUNT(DISTINCT pv.sessionId) as uniqueCount')
      .groupBy('YEARWEEK(pv.createdAt)')
      .orderBy('week', 'ASC')
      .getRawMany();
  }
  private async getPageViewsByMonth(queryBuilder, startDate: Date, endDate: Date): Promise<any[]> {
    return queryBuilder
      .select('DATE_FORMAT(pv.createdAt, "%Y-%m") as month')
      .addSelect('COUNT(*) as count')
      .addSelect('COUNT(DISTINCT pv.sessionId) as uniqueCount')
      .groupBy('DATE_FORMAT(pv.createdAt, "%Y-%m")')
      .orderBy('month', 'ASC')
      .getRawMany();
  }
  private async getPageViewsByUrl(queryBuilder, limit: number = 10, offset: number = 0): Promise<any[]> {
    return queryBuilder
      .select('pv.url')
      .addSelect('COUNT(*) as count')
      .addSelect('COUNT(DISTINCT pv.sessionId) as uniqueCount')
      .groupBy('pv.url')
      .orderBy('count', 'DESC')
      .limit(limit)
      .offset(offset)
      .getRawMany();
  }
  private async getPageViewsByReferrer(queryBuilder, limit: number = 10, offset: number = 0): Promise<any[]> {
    return queryBuilder
      .select('pv.referrer')
      .addSelect('COUNT(*) as count')
      .addSelect('COUNT(DISTINCT pv.sessionId) as uniqueCount')
      .groupBy('pv.referrer')
      .orderBy('count', 'DESC')
      .limit(limit)
      .offset(offset)
      .getRawMany();
  }
  private async getPageViewsByBrowser(queryBuilder, limit: number = 10, offset: number = 0): Promise<any[]> {
    return queryBuilder
      .select('pv.browser')
      .addSelect('COUNT(*) as count')
      .addSelect('COUNT(DISTINCT pv.sessionId) as uniqueCount')
      .groupBy('pv.browser')
      .orderBy('count', 'DESC')
      .limit(limit)
      .offset(offset)
      .getRawMany();
  }
  private async getPageViewsByOS(queryBuilder, limit: number = 10, offset: number = 0): Promise<any[]> {
    return queryBuilder
      .select('pv.os')
      .addSelect('COUNT(*) as count')
      .addSelect('COUNT(DISTINCT pv.sessionId) as uniqueCount')
      .groupBy('pv.os')
      .orderBy('count', 'DESC')
      .limit(limit)
      .offset(offset)
      .getRawMany();
  }
  private async getPageViewsByDevice(queryBuilder, limit: number = 10, offset: number = 0): Promise<any[]> {
    return queryBuilder
      .select('pv.device')
      .addSelect('COUNT(*) as count')
      .addSelect('COUNT(DISTINCT pv.sessionId) as uniqueCount')
      .groupBy('pv.device')
      .orderBy('count', 'DESC')
      .limit(limit)
      .offset(offset)
      .getRawMany();
  }
  private async getPageViewsByCountry(queryBuilder, limit: number = 10, offset: number = 0): Promise<any[]> {
    return queryBuilder
      .select('pv.country')
      .addSelect('COUNT(*) as count')
      .addSelect('COUNT(DISTINCT pv.sessionId) as uniqueCount')
      .groupBy('pv.country')
      .orderBy('count', 'DESC')
      .limit(limit)
      .offset(offset)
      .getRawMany();
  }

  async getWebsiteStats(id: number, query: WebsiteQueryDto): Promise<any> {
    const website = await this.getWebsiteById(id);
    const { startDate, endDate } = this.getDateRange(query.timeRange, query.startDate, query.endDate);
    let queryBuilder = this.pageViewRepository.createQueryBuilder('pv')
      .where('pv.websiteId = :websiteId', { websiteId: website.id })
      .andWhere('pv.createdAt BETWEEN :startDate AND :endDate', { startDate, endDate });
    if (query.url) queryBuilder = queryBuilder.andWhere('pv.url LIKE :url', { url: `%${query.url}%` });
    if (query.referrer) queryBuilder = queryBuilder.andWhere('pv.referrer LIKE :referrer', { referrer: `%${query.referrer}%` });
    if (query.country) queryBuilder = queryBuilder.andWhere('pv.country = :country', { country: query.country });
    if (query.browser) queryBuilder = queryBuilder.andWhere('pv.browser = :browser', { browser: query.browser });
    if (query.os) queryBuilder = queryBuilder.andWhere('pv.os = :os', { os: query.os });
    if (query.device) queryBuilder = queryBuilder.andWhere('pv.device = :device', { device: query.device });
    const [
      totalPageViews,
      uniquePageViews,
      totalSessions,
      bounceSessions
    ] = await Promise.all([
      queryBuilder.getCount(),
      queryBuilder.clone().select('COUNT(DISTINCT pv.sessionId)', 'unique').getRawOne().then(r => Number(r.unique) || 0),
      queryBuilder.clone().select('COUNT(DISTINCT pv.sessionId)', 'total').getRawOne().then(r => Number(r.total) || 0),
      this.pageViewRepository.createQueryBuilder('pv')
        .select('pv.sessionId')
        .addSelect('COUNT(*) as cnt')
        .where('pv.websiteId = :websiteId', { websiteId: website.id })
        .andWhere('pv.createdAt BETWEEN :startDate AND :endDate', { startDate, endDate })
        .groupBy('pv.sessionId')
        .having('COUNT(*) = 1')
        .getCount()
    ]);
    const bounceRate = totalSessions > 0 ? Math.round((bounceSessions / totalSessions) * 1000) / 10 : 0;
    const [
      dataByDay,
      dataByWeek,
      dataByMonth,
      topUrls,
      topReferrers,
      topBrowsers,
      topOS,
      topDevices,
      topCountries
    ] = await Promise.all([
      this.getPageViewsByDay(queryBuilder.clone(), startDate, endDate),
      this.getPageViewsByWeek(queryBuilder.clone(), startDate, endDate),
      this.getPageViewsByMonth(queryBuilder.clone(), startDate, endDate),
      this.getPageViewsByUrl(queryBuilder.clone(), 10, 0),
      this.getPageViewsByReferrer(queryBuilder.clone(), 10, 0),
      this.getPageViewsByBrowser(queryBuilder.clone(), 10, 0),
      this.getPageViewsByOS(queryBuilder.clone(), 10, 0),
      this.getPageViewsByDevice(queryBuilder.clone(), 10, 0),
      this.getPageViewsByCountry(queryBuilder.clone(), 10, 0)
    ]);
    const normalize = (arr, key) => (arr || []).map(item => ({
      [key]: item[key] ?? item[Object.keys(item)[0]],
      count: Number(item.count) || 0
    }));
    return {
      summary: {
        totalPageViews: Number(totalPageViews) || 0,
        uniquePageViews: Number(uniquePageViews) || 0,
        bounceRate: Number(bounceRate)
      },
      data: (query.groupBy === 'week' ? dataByWeek : query.groupBy === 'month' ? dataByMonth : dataByDay).map(d => ({
        date: d.date || d.week || d.month,
        pv: Number(d.count) || 0,
        uv: Number(d.uniqueCount) || 0,
      })),
      referrers: normalize(topReferrers, 'referrer'),
      devices: normalize(topDevices, 'device'),
      browsers: normalize(topBrowsers, 'browser'),
      os: normalize(topOS, 'os'),
      countries: normalize(topCountries, 'country'),
      urls: normalize(topUrls, 'url')
    };
  }

  async getWebsiteSummary(id: number, query: WebsiteQueryDto): Promise<any> {
    const website = await this.getWebsiteById(id);
    const { startDate, endDate } = this.getDateRange(query.timeRange, query.startDate, query.endDate);
    let queryBuilder = this.pageViewRepository.createQueryBuilder('pv')
      .where('pv.websiteId = :websiteId', { websiteId: website.id })
      .andWhere('pv.createdAt BETWEEN :startDate AND :endDate', { startDate, endDate });
    const [
      totalPageViews,
      uniquePageViews,
      totalSessions,
      bounceSessions
    ] = await Promise.all([
      queryBuilder.getCount(),
      queryBuilder.clone().select('COUNT(DISTINCT pv.sessionId)', 'unique').getRawOne().then(r => Number(r.unique) || 0),
      queryBuilder.clone().select('COUNT(DISTINCT pv.sessionId)', 'total').getRawOne().then(r => Number(r.total) || 0),
      this.pageViewRepository.createQueryBuilder('pv')
        .select('pv.sessionId')
        .addSelect('COUNT(*) as cnt')
        .where('pv.websiteId = :websiteId', { websiteId: website.id })
        .andWhere('pv.createdAt BETWEEN :startDate AND :endDate', { startDate, endDate })
        .groupBy('pv.sessionId')
        .having('COUNT(*) = 1')
        .getCount()
    ]);
    const bounceRate = totalSessions > 0 ? Math.round((bounceSessions / totalSessions) * 1000) / 10 : 0;
    const avgPageViews = uniquePageViews > 0 ? Math.round((totalPageViews / uniquePageViews) * 10) / 10 : 0;
    return {
      totalPageViews: Number(totalPageViews) || 0,
      uniquePageViews: Number(uniquePageViews) || 0,
      avgPageViews,
      bounceRate: Number(bounceRate)
    };
  }

  async getWebsiteTrend(id: number, query: WebsiteQueryDto): Promise<any[]> {
    const website = await this.getWebsiteById(id);
    const { startDate, endDate } = this.getDateRange(query.timeRange, query.startDate, query.endDate);
    const groupBy = query.groupBy || 'day';
    let queryBuilder = this.pageViewRepository.createQueryBuilder('pv')
      .where('pv.websiteId = :websiteId', { websiteId: website.id })
      .andWhere('pv.createdAt BETWEEN :startDate AND :endDate', { startDate, endDate });
    let trendData: any[] = [];
    if (groupBy === 'week') {
      trendData = await this.getPageViewsByWeek(queryBuilder.clone(), startDate, endDate);
    } else if (groupBy === 'month') {
      trendData = await this.getPageViewsByMonth(queryBuilder.clone(), startDate, endDate);
    } else {
      trendData = await this.getPageViewsByDay(queryBuilder.clone(), startDate, endDate);
    }
    return trendData.map(d => ({
      date: d.date || d.week || d.month,
      pv: Number(d.count) || 0,
      uv: Number(d.uniqueCount) || 0,
    }));
  }
} 